Instead of designing the laser for stable mode-locking, we want to investigate how we could apply state feedback to the pump power to stabilize it.
We start from linearized laser-dynamics:
In [1]:
from IPython.display import display
import sympy as sym
sym.init_printing(use_latex='mathjax')
w0, zeta, rho, Toc = sym.symbols('omega_0 zeta rho T_{oc}', real=True)
x = sym.MatrixSymbol('x', 2, 1)
xdot = sym.MatrixSymbol('xdot', 2, 1)
u = sym.MatrixSymbol('u', 1, 1)
A = sym.Matrix([[-2 * w0 * zeta, -w0],
[w0, 0, ]])
B = sym.Matrix([[rho * w0],
[0]])
xdot_ = sym.Eq(xdot, A * x + B * u)
xdot_
Out[1]:
where
$ \vec{x} = \left[ \begin{array}{ccc} \delta \dot{P} / \omega_0 \\ \delta P \end{array}\right] $ and $ u = \left[ \delta P_P \right] $. $\rho$ is the internal slope efficiency, $\omega_0$ is the natural frequency, and $\zeta$ is the damping ratio of the system.
The output equation is chosen such that it provides the normal output (i.e. $\delta P_{out}$) and additionally the full state:
In [2]:
y = sym.MatrixSymbol('y', 3, 1)
C = sym.Matrix([[0, Toc],
[1, 0],
[0, 1]])
y_ = sym.Eq(y, C * x)
y_
Out[2]:
where $T_{oc}$ is the output-coupling ratio.
Now we apply feedback (almost) as shown in the figure. This means that
$ u = k_r \cdot r - K \cdot \vec{y} $
where $r$ is the control signal, $k_r$ is a constant, and $K = \left[0, k_1, k_2 \right]$ is the feedback matrix. Inserting this into the state equations yields:
$ \dot{\vec{x}} = (A - B K C) \vec{x} + B k_r r $
which is a new system $\dot{\vec{x}} = A' \vec{x} + B' r$ with
$ \begin{align*} A' &= A - B K C \\ B' &= B k_r \end{align*} $
In [3]:
kr = sym.Symbol('k_r')
r = sym.MatrixSymbol('r', 1, 1)
Ap = sym.MatrixSymbol("A'", 2, 2)
Bp = sym.MatrixSymbol("B'", 2, 1)
k1, k2 = sym.symbols('k_1 k_2', real=True)
K = sym.Matrix([[0, k1, k2]])
Ap_ = sym.Eq(Ap, A - B * K * C)
Bp_ = sym.Eq(Bp, B * kr)
display(Ap_, Bp_)
Our goal is to to choose $K$ so as to obtain arbitrary poles in the new system. The eigenvalues of $A'$ are:
In [4]:
ev = Ap_.rhs.eigenvects()
vals = [v[0] for v in ev]
gamma = sym.Symbol("\gamma", real=True)
gamma_ = sym.Eq(gamma, k1 * rho / 2 + zeta)
nu = sym.Symbol('\\nu', real=True)
nu_ = sym.Eq(nu, (k1 * rho)**2 / 4 + rho * (k1 * zeta - k2) + zeta**2 - 1)
vals = [sym.simplify(v.subs([(k2, sym.solve(nu_, k2)[0]),
(zeta, sym.solve(gamma_, zeta)[0])]))
for v in vals]
display(*vals)
where
In [5]:
display(gamma_, nu_)
To obtain the poles as advertised above, $K$ has to be chosen as follows:
In [6]:
k1_ = sym.Eq(k1, sym.solve(gamma_, k1)[0])
k2_ = sym.Eq(k2, sym.solve(nu_.subs(k1_.lhs, k1_.rhs), k2)[0])
sym.Eq(sym.MatrixSymbol('K', 1, 3),
K.subs(k1_.lhs, k1_.rhs).subs(k2_.lhs, k2_.rhs))
Out[6]:
Finally, we require that the unit-step response on $\delta P$ is $\rho$, i.e. the same as in the uncontrolled system. To achieve that, we choose $k_r$ as follows:
In [7]:
dPstep = sym.simplify(-Ap_.rhs**-1 * Bp_.rhs)[1]
dPstep = dPstep.subs(k2_.lhs, k2_.rhs)
kr_ = sym.Eq(kr, sym.solve(sym.Eq(rho, dPstep), kr)[0])
kr_
Out[7]: